Linux 0.12
1. printf
linux-0.12/init/main.c
,
static int printf(const char *fmt, ...)
{
va_list args;
int i;
va_start(args, fmt);
write(1,printbuf,i=vsprintf(printbuf, fmt, args));
va_end(args);
return i;
}
1.1 va_start和va_end
linux-0.12/include/stdarg.h
,
typedef char *va_list;
#ifndef __sparc__
#define va_start(AP, LASTARG) \
(AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
#else
#define va_start(AP, LASTARG) \
(__builtin_saveregs (), \
AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
#endif
__sparc__
,sparc是可扩充处理器架构(Scalable Processor ARChitecture)的缩写,是一种精简指令集计算机指令集架构va_start
1.2 write
write
在linux-0.12/include/unistd.h
声明,
int write(int fildes, const char * buf, off_t count);
通过_syscall3
,执行一个系统调用,
#define __LIBRARY__
#include <unistd.h>
_syscall3(int,write,int,fd,const char *,buf,off_t,count)
_syscall3
在linux-0.12/include/unistd.h
定义,
#define _syscall3(type,name,atype,a,btype,b,ctype,c) \
type name(atype a,btype b,ctype c) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \
if (__res>=0) \
return (type) __res; \
errno=-__res; \
return -1; \
}
__NR_##name
,此时值为__NR_write
,即为4,
#define __NR_write 4
int $0x80
,执行系统调用_system_call:
,其中call _sys_call_table(,%eax,4)
实现程序跳转,跳转地址为_sys_call_table + %eax * 4
,eax
存放的是系统调用功能号,即跳转到函数sys_write
。关于系统调用见:Linux 0.12系统调用。
1.3 sys_write
系统调用函数sys_write
在linux-0.12/fs/read_write.c
文件中定义,
int sys_write(unsigned int fd,char * buf,int count)
{
struct file * file;
struct m_inode * inode;
if (fd>=NR_OPEN || count <0 || !(file=current->filp[fd]))
return -EINVAL;
if (!count)
return 0;
inode=file->f_inode;
if (inode->i_pipe)
return (file->f_mode&2)?write_pipe(inode,buf,count):-EIO;
if (S_ISCHR(inode->i_mode))
return rw_char(WRITE,inode->i_zone[0],buf,count,&file->f_pos);
if (S_ISBLK(inode->i_mode))
return block_write(inode->i_zone[0],&file->f_pos,buf,count);
if (S_ISREG(inode->i_mode))
return file_write(inode,file,buf,count);
printk("(Write)inode->i_mode=%06o\n\r",inode->i_mode);
return -EINVAL;
}
根据设备不同,调用不同的写入函数。
2. printk
printf
并不能直接在内核中使用,因为内核代码中不能直接使用专用于用户模式的fs
段寄存器(fs
指向局部数据段),而内核态运行的程序要打印的信息在内核数据段中。
因此,内核需要实现自己的打印函数,即printk
。printk
在linux-0.12/kernel/printk.c
文件中定义,
/*
* When in kernel-mode, we cannot use printf, as fs is liable to
* point to 'interesting' things. Make a printf with fs-saving, and
* all is well.
*/
#include <stdarg.h>
#include <stddef.h>
#include <linux/kernel.h>
static char buf[1024];
extern int vsprintf(char * buf, const char * fmt, va_list args);
int printk(const char *fmt, ...)
{
va_list args;
int i;
va_start(args, fmt);
i=vsprintf(buf,fmt,args);
va_end(args);
console_print(buf);
return i;
}
2.1 vsprintf
linux-0.12/kernel/vsprintf.c
,
int vsprintf(char *buf, const char *fmt, va_list args)
{
int len;
int i;
char * str;
char *s;
int *ip;
int flags; /* flags to number() */
int field_width; /* width of output field */
int precision; /* min. # of digits for integers; max
number of chars for from string */
int qualifier; /* 'h', 'l', or 'L' for integer fields */
for (str=buf ; *fmt ; ++fmt) {
if (*fmt != '%') {
*str++ = *fmt;
continue;
}
/* process flags */
flags = 0;
repeat:
++fmt; /* this also skips first '%' */
switch (*fmt) {
case '-': flags |= LEFT; goto repeat;
case '+': flags |= PLUS; goto repeat;
case ' ': flags |= SPACE; goto repeat;
case '#': flags |= SPECIAL; goto repeat;
case '0': flags |= ZEROPAD; goto repeat;
}
/* get field width */
field_width = -1;
if (is_digit(*fmt))
field_width = skip_atoi(&fmt);
else if (*fmt == '*') {
/* it's the next argument */
field_width = va_arg(args, int);
if (field_width < 0) {
field_width = -field_width;
flags |= LEFT;
}
}
/* get the precision */
precision = -1;
if (*fmt == '.') {
++fmt;
if (is_digit(*fmt))
precision = skip_atoi(&fmt);
else if (*fmt == '*') {
/* it's the next argument */
precision = va_arg(args, int);
}
if (precision < 0)
precision = 0;
}
/* get the conversion qualifier */
qualifier = -1;
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
qualifier = *fmt;
++fmt;
}
switch (*fmt) {
case 'c':
if (!(flags & LEFT))
while (--field_width > 0)
*str++ = ' ';
*str++ = (unsigned char) va_arg(args, int);
while (--field_width > 0)
*str++ = ' ';
break;
case 's':
s = va_arg(args, char *);
len = strlen(s);
if (precision < 0)
precision = len;
else if (len > precision)
len = precision;
if (!(flags & LEFT))
while (len < field_width--)
*str++ = ' ';
for (i = 0; i < len; ++i)
*str++ = *s++;
while (len < field_width--)
*str++ = ' ';
break;
case 'o':
str = number(str, va_arg(args, unsigned long), 8,
field_width, precision, flags);
break;
case 'p':
if (field_width == -1) {
field_width = 8;
flags |= ZEROPAD;
}
str = number(str,
(unsigned long) va_arg(args, void *), 16,
field_width, precision, flags);
break;
case 'x':
flags |= SMALL;
case 'X':
str = number(str, va_arg(args, unsigned long), 16,
field_width, precision, flags);
break;
case 'd':
case 'i':
flags |= SIGN;
case 'u':
str = number(str, va_arg(args, unsigned long), 10,
field_width, precision, flags);
break;
case 'n':
ip = va_arg(args, int *);
*ip = (str - buf);
break;
default:
if (*fmt != '%')
*str++ = '%';
if (*fmt)
*str++ = *fmt;
else
--fmt;
break;
}
}
*str = '\0';
return str-buf;
}
2.2 console.c
linux-0.12/kernel/console.c
,
void console_print(const char * b)
{
int currcons = fg_console;
char c;
while (c = *(b++)) {
if (c == 10) {
cr(currcons);
lf(currcons);
continue;
}
if (c == 13) {
cr(currcons);
continue;
}
if (x>=video_num_columns) {
x -= video_num_columns;
pos -= video_size_row;
lf(currcons);
}
__asm__("movb %2,%%ah\n\t"
"movw %%ax,%1\n\t"
::"a" (c),
"m" (*(short *)pos),
"m" (attr)
:"ax");
pos += 2;
x++;
}
set_cursor(currcons);
}